/*********************************************************************************
 * PROJECT: MiMic
 * --------------------------------------------------------------------------------
 *
 * This file is part of MiMic
 * Copyright (C)2011 Ryo Iizuka
 *
 * MiMic is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published
 * by　the Free Software Foundation, either version 3 of the　License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * For further information please contact.
 *	http://nyatla.jp/
 *	<airmail(at)ebony.plala.or.jp> or <nyatla(at)nyatla.jp>
 *
 *********************************************************************************/
#include "NyLPC_cTcpSocket_protected.h"
#include "NyLPC_stdlib.h"
#include "task.h"
#include "NyLPC_cUipService_protected.h"


static NyLPC_TUInt32 iss32=3939;
#define SIZE_OF_IPv4_TCPIP_HEADER 40


static void sendRst(NyLPC_TcTcpSocket_t* i_inst);


/**
 * ソケットステータスを元に、IPパケットを構成します。
 * この関数は、ロック状態でコールしてください。
 */
static void setPacket(const NyLPC_TcTcpSocket_t* i_inst,NyLPC_TcIPv4Payload_t* i_payload,NyLPC_TUInt8 i_tcpf,const void* i_buf,NyLPC_TUInt16 i_len)
{
	void* buf;
	switch(i_tcpf){
	case TCP_PSH|TCP_ACK:
		buf=NyLPC_cIPv4Payload_initTcpTx(i_payload,0x05,((UIP_TCPH_LEN) / 4),i_len);
		NyLPC_cIPv4Payload_setTcpTxHeaderByConnection(i_payload,&(i_inst->uip_connr),TCP_ACK|TCP_PSH);
		//bufの書き込み
		memcpy(buf,i_buf,i_len);
		break;
	case TCP_ACK:
	case TCP_FIN|TCP_ACK:
	case TCP_RST|TCP_ACK:
		NyLPC_cIPv4Payload_initTcpTx(i_payload,0x05,((UIP_TCPH_LEN) / 4),0);
		NyLPC_cIPv4Payload_setTcpTxHeaderByConnection(i_payload,&(i_inst->uip_connr),i_tcpf);
		break;
	case TCP_SYN|TCP_ACK:
		NyLPC_cIPv4Payload_initTcpTx(i_payload,0x05,((UIP_TCPH_LEN + TCP_OPT_MSS_LEN) / 4),0);
		NyLPC_cIPv4Payload_setTcpTxHeaderByConnection(i_payload,&(i_inst->uip_connr),i_tcpf);
		//MSSの設定(OPTION領域のアドレス0)
		NyLPC_TTcpHeader_setMmsOpt((NyLPC_TUInt8*)(i_payload->payload.tcp+1),i_inst->uip_connr.default_mss);
		break;
	case TCP_SYN:
		NyLPC_cIPv4Payload_initTcpTx(i_payload,0x05,((UIP_TCPH_LEN + TCP_OPT_MSS_LEN) / 4),0);
		NyLPC_cIPv4Payload_setTcpTxHeaderByConnection(i_payload,&(i_inst->uip_connr),i_tcpf);
		//MSSの設定(OPTION領域のアドレス0)
		NyLPC_TTcpHeader_setMmsOpt((NyLPC_TUInt8*)(i_payload->payload.tcp+1),i_inst->uip_connr.default_mss);
		break;
	default:
		NyLPC_Abort();
	}
	NyLPC_cIPv4Payload_setTcpWnd(i_payload,NyLPC_cFifoBuffer_getSpace(&(i_inst->rxbuf)));
	NyLPC_cIPv4Payload_closeTcpTxPacket(i_payload);
	return;
}
/**
 * 指定した送信パケットがACK済であるか調べる。
 */
static NyLPC_TBool isPacketAcked(NyLPC_TcTcpSocket_t* i_inst,NyLPC_TUInt32 i_sq)
{
	int rp;
	struct NyLPC_TcTcpSocket_TxQItem* q=i_inst->txbuf.txq;
	rp=i_inst->txbuf.rp;
	while(rp!=i_inst->txbuf.wp){
		if(q[rp].ackno==i_sq){
			return NyLPC_TBool_FALSE;
		}
		rp=(rp+1)%NyLPC_TcTcpSocket_NUMBER_OF_TXQ;
	}
	return NyLPC_TBool_TRUE;
}
/**
 * 送信キューからi_sq以前に送信したパケットを除外して、残り個数を返却する。
 */
static int getNumOfSending(NyLPC_TcTcpSocket_t* i_inst,NyLPC_TUInt32 i_sq)
{
	int rp,n;
	struct NyLPC_TcTcpSocket_TxQItem* q=i_inst->txbuf.txq;
	rp=i_inst->txbuf.rp;
	n=0;
	while(rp!=i_inst->txbuf.wp){
		if(q[rp].ackno==i_sq){
			return n;
		}
		n++;
		rp=(rp+1)%NyLPC_TcTcpSocket_NUMBER_OF_TXQ;
	}
	return n;
}
/**
 * この関数は、コネクションをリセットします。
 * ロック状態でコールしてください。
 * 関数は、現在バッファにある再送信待ちデータを開放します。
 */
static void resetTxQWithUnlock(NyLPC_TcTcpSocket_t* i_inst)
{
	int i,l;
	struct NyLPC_TcTcpSocket_TxQItem* q=i_inst->txbuf.txq;
	void* dlist[NyLPC_TcTcpSocket_NUMBER_OF_TXQ];

	l=0;
	while(i_inst->txbuf.rp!=i_inst->txbuf.wp){
		dlist[l]=NyLPC_cIPv4Payload_detachBuf(&(q[i_inst->txbuf.rp].data));
		l++;
		i_inst->txbuf.rp=(i_inst->txbuf.rp+1)%NyLPC_TcTcpSocket_NUMBER_OF_TXQ;
	}
	i_inst->txbuf.rp=i_inst->txbuf.wp=0;
	//ロック解除
	NyLPC_cMutex_unlock(&(i_inst->_smutex));
	//セーブしたバッファを開放
	for(i=0;i<l;i++){
		NyLPC_cUipService_releaseTxBuf(dlist[i]);
	}
	return;
}
/**
 * TXバッファの再送パケットのACK番号を更新します。
 * ロックして実行してください。
 * @param i_ackno
 * ネットワークオーダーのACK番号
 */
static void updateTxAck(NyLPC_TcTcpSocket_t* i_inst,NyLPC_TUInt32 i_ackno)
{
	NyLPC_TUInt8 rp;
	struct NyLPC_TcTcpSocket_TxQItem* q=i_inst->txbuf.txq;
	NyLPC_ArgAssert(i_inst!=NULL);
	rp=i_inst->txbuf.rp;
	while(rp!=i_inst->txbuf.wp){
		NyLPC_cIPv4Payload_updateAckNo(&(q[rp].data),i_ackno);
		rp=(rp+1)%NyLPC_TcTcpSocket_NUMBER_OF_TXQ;
	}
}

/**
 * 送信キューをパラメータにあわせて更新します。
 * リングバッファのrp->wp-1までをチェックして、sqに等しいi_sq以前のパケットバッファをo_dlistへ返します。
 *
 */
static int updateTxQByIndex(NyLPC_TcTcpSocket_t* i_inst,NyLPC_TUInt32 i_sq,void* o_dlist[])
{
	int rp,n;
	struct NyLPC_TcTcpSocket_TxQItem* q=i_inst->txbuf.txq;
	//ロック状態なう
	rp=i_inst->txbuf.rp;
	n=0;
	while(rp!=i_inst->txbuf.wp){
		o_dlist[n]=NyLPC_cIPv4Payload_getBuf(&(q[rp].data));
		if(q[rp].ackno==i_sq){
			i_inst->txbuf.rp=(rp+1)%NyLPC_TcTcpSocket_NUMBER_OF_TXQ;
			return n+1;
		}
		n++;
		rp=(rp+1)%NyLPC_TcTcpSocket_NUMBER_OF_TXQ;
	}
	return 0;
}



/**
 * 空きキューを1個返します。
 * 空きキューの
 */
static struct NyLPC_TcTcpSocket_TxQItem* getTxQ(NyLPC_TcTcpSocket_t* i_inst,NyLPC_TcStopwatch_t* i_timer)
{
	int i;
	struct NyLPC_TcTcpSocket_TxQItem* q=i_inst->txbuf.txq;
	while(!NyLPC_cStopwatch_isExpired(i_timer)){
		//クローズドに遷移してしまったら、エラーである。
		if(i_inst->tcpstateflags==UIP_CLOSED){
			return NULL;
		}
		//キューの空きをチェック。wp+1==rpなら、キューがいっぱい。rp==wpなら、キューが空。
		if(((i_inst->txbuf.wp+1)%NyLPC_TcTcpSocket_NUMBER_OF_TXQ)==i_inst->txbuf.rp){
			//一時的なアンロック
			NyLPC_cMutex_unlock(&(i_inst->_smutex));
			//タスクスイッチ
			taskYIELD();
			//ロック
			NyLPC_cMutex_lock(&(i_inst->_smutex));
			continue;
		}
		i=i_inst->txbuf.wp;
		i_inst->txbuf.wp=(i+1)%NyLPC_TcTcpSocket_NUMBER_OF_TXQ;
		return &(q[i]);
	}
	//失敗。タイムアウト。
	return NULL;
}






/**********************************************************************
 * public 関数
 **********************************************************************/


NyLPC_TBool NyLPC_cTcpSocket_initialize(NyLPC_TcTcpSocket_t* i_inst,void* i_rbuf,NyLPC_TUInt16 i_rbuf_len)
{
	int i;
	NyLPC_TcUipService_t* srv=_NyLPC_TcUipService_inst;
	//uipサービスは初期化済であること。
	NyLPC_Assert(NyLPC_TcUipService_isInitService());

	NyLPC_cFifoBuffer_initialize(&(i_inst->rxbuf),i_rbuf,i_rbuf_len);
	NyLPC_AbortIfNot(NyLPC_cMutex_initialize(&(i_inst->_smutex)));
	i_inst->tcpstateflags=UIP_CLOSED;
	i_inst->txbuf.rp=i_inst->txbuf.wp=0;
	for(i=0;i<NyLPC_TcTcpSocket_NUMBER_OF_TXQ;i++){
		NyLPC_cIPv4Payload_initialize(&(i_inst->txbuf.txq[i].data));
	}
	//管理リストへ登録。
	return NyLPC_cIPv4_addSocket(&(srv->_tcpv4),i_inst);
}
void NyLPC_cTcpSocket_finalize(NyLPC_TcTcpSocket_t* i_inst,void* i_rbuf,NyLPC_TUInt16 i_rbuf_len)
{
	int i;
	NyLPC_TcUipService_t* srv=_NyLPC_TcUipService_inst;
	NyLPC_Assert(NyLPC_TcUipService_isInitService());
	//uipサービスは初期化済であること。
	if(!NyLPC_cIPv4_removeSocket(&(srv->_tcpv4),i_inst)){
		//削除失敗、それは死を意味する。
		NyLPC_Abort();
	}
	//開放漏れの保険
	if(i_inst->txbuf.rp!=i_inst->txbuf.wp){
		NyLPC_cMutex_lock(&(i_inst->_smutex));
		resetTxQWithUnlock(i_inst);
	}
	for(i=0;i<NyLPC_TcTcpSocket_NUMBER_OF_TXQ;i++){
		NyLPC_cIPv4Payload_finalize(&(i_inst->txbuf.txq[i].data));
	}
	NyLPC_cFifoBuffer_finalize(&(i_inst->rxbuf));
	NyLPC_cMutex_finalize(&(i_inst->_smutex));

	return;
}

/**
 * この関数は、cIPv4Tcpが呼び出すシステムAPIです。
 * uipコアタスクが実行します。
 * ソケットを、SYNパケットで初期化して、UIP_SYN_RECV状態にします。
 * @return
 * 遷移に成功すると、TRUEを返します。
 */
NyLPC_TBool NyLPC_cTcpSocket_setSynPayload(NyLPC_TcTcpSocket_t* i_inst,const NyLPC_TcIPv4Config_t* i_config,const NyLPC_TcIPv4Payload_t* i_ipp)
{
	NyLPC_TUInt16 tmp16;
	//ソケットが無効であること。
	NyLPC_cMutex_lock(&(i_inst->_smutex));
	if(i_inst->tcpstateflags==UIP_CLOSED)
	{
		/* Fill in the necessary fields for the new connection. */
		i_inst->uip_connr.rto = UIP_DEFAULT_IP_RTO;
		i_inst->uip_connr.lport = i_ipp->payload.tcp->destport;
		i_inst->uip_connr.rport = i_ipp->payload.tcp->srcport;
		i_inst->uip_connr.ripaddr=i_ipp->header->srcipaddr;
		i_inst->uip_connr.snd_nxt32=iss32;
		i_inst->uip_connr.lipaddr=&(i_config->ip_addr);
		/* rcv_nxt should be the seqno from the incoming packet + 1. */
		i_inst->uip_connr.rcv_nxt32= NyLPC_ntohl(i_ipp->payload.tcp->seqno32)+1;
		//MSSの設定
		i_inst->uip_connr.default_mss=i_inst->uip_connr.peer_mss=i_config->default_mss;
		if(NyLPC_TTcpHeader_getTcpMmsOpt(i_ipp->payload.tcp,&tmp16)){
			i_inst->uip_connr.peer_mss=tmp16;
		}
		i_inst->uip_connr.peer_win=0;
		NyLPC_cFifoBuffer_clear(&(i_inst->rxbuf));
		//前回のデータが残っていた場合の保険
		if(i_inst->txbuf.rp!=i_inst->txbuf.wp){
			resetTxQWithUnlock(i_inst);
		}else{
			NyLPC_cMutex_unlock(&(i_inst->_smutex));
		}
		//ここでステータスがかわる。
		i_inst->tcpstateflags = UIP_SYN_RCVD;
		return NyLPC_TBool_TRUE;
	}
	NyLPC_cMutex_unlock(&(i_inst->_smutex));
	return NyLPC_TBool_FALSE;
}
/**
 * sq番のTxがキューから消え去るのを待ちます。
 * この関数は、アンロック状態でコールしてください。
 * <div>
 * パケットがキューからなくなる条件は、以下の２つです。
 * <ul>
 * <li>ACKを受信してパケットキューが更新された。</li>
 * <li>RSTを受信して(CLOSEDに遷移して)、キューがクリアされた。</li>
 * <li>送信タイムアウトで関数が(CLOSEDに遷移させて)キューをクリアした。</li>
 * </ul>
 * </div>
 * @param i_wait_msec
 * @return
 * 1番目の条件でパケットが消失したときのみ、TRUEを返します。
 * 失敗した場合、TCPステータスがCLOSEDでなければ、RSTを送信してステータスをCLOSEDにします。
 */
static NyLPC_TBool waitForTxRemove(NyLPC_TcTcpSocket_t* i_inst,NyLPC_TUInt32 i_sq,NyLPC_TcStopwatch_t* i_timer)
{
	NyLPC_TUInt8 f;
	NyLPC_cMutex_lock(&(i_inst->_smutex));
	while(!NyLPC_cStopwatch_isExpired(i_timer)){
		//パケットが送信中か調べる。
		if(!isPacketAcked(i_inst,i_sq)){
			//まだある場合は、タスクスイッチを繰り返して消失を待つ。
			NyLPC_cMutex_unlock(&(i_inst->_smutex));
			taskYIELD();
			NyLPC_cMutex_lock(&(i_inst->_smutex));
			continue;
		}
		//なくなった場合は、原因を調べる。
		f=i_inst->tcpstateflags;
		NyLPC_cMutex_unlock(&(i_inst->_smutex));
		return (f==UIP_CLOSED)?NyLPC_TBool_FALSE:NyLPC_TBool_TRUE;
	}
	NyLPC_cMutex_unlock(&(i_inst->_smutex));
	return NyLPC_TBool_FALSE;
}




/**
 * 再送信処理をセットして、パケットを送信します。
 * この関数は「アンロック状態で」実行してください。
 * @param i_len
 * 送信データサイズを指定します。
 * この番号は、シーケンス番号の加算値ではありませんので、注意をしてください。
 * @return
 * <ul>
 * <li>n=-1:送信キューへの投入に失敗した。</li>
 * <li>n>=0:nバイトのデータを送信キューへの投入することに成功した。</li>
 * </ul>
 * 送信キューに失敗する理由は2つあります。1つは、TXバッファがフルでタイムアウト。もうひとつは、非同期なコネクリョンのリセットです。
 * 失敗した場合、TCPステータスがCLOSEDでなければ、RSTを送信してステータスをCLOSEDにします。
 */
static NyLPC_TInt32 sendWithRetransmit(NyLPC_TcTcpSocket_t* i_inst,NyLPC_TUInt8 i_tcpf,const void* i_buf,NyLPC_TUInt16 i_len,NyLPC_TcStopwatch_t* i_timer,NyLPC_TUInt32* o_ack)
{
	struct NyLPC_TcTcpSocket_TxQItem* txq;
	NyLPC_TUInt16 s;
	void* buf;
	NyLPC_TUInt32 next_ack;


	//送信バッファを取得
	buf=NyLPC_cUipService_allocTxBuf(i_len+(SIZE_OF_IPv4_TCPIP_HEADER),&s);
	NyLPC_cMutex_lock(&(i_inst->_smutex));
	//ペイロードがある場合のみ、相手のwindowサイズが0以上になるのを待つ。
	if(i_len>0){
		while(i_inst->uip_connr.peer_win==0){
			NyLPC_cMutex_unlock(&(i_inst->_smutex));
			//時間切れならエラー。
			if(NyLPC_cStopwatch_isExpired(i_timer)){
				return -1;
			}
			taskYIELD();
			NyLPC_cMutex_lock(&(i_inst->_smutex));
		}
	}
	//送信キューの取得
	txq=getTxQ(i_inst,i_timer);
	//送信キューが取れなかった。
	if(txq==NULL){
		//シーケンス番号をロールバックできないので、エラーとする。
		NyLPC_cMutex_unlock(&(i_inst->_smutex));
		NyLPC_cUipService_releaseTxBuf(buf);
		return -1;
	}
	//IPv4ペイロードの書き込み
	NyLPC_cIPv4Payload_setTxBuf(&(txq->data),buf);

	//送信バッファを基準とした送信サイズを計算
	s-=SIZE_OF_IPv4_TCPIP_HEADER;
	//送信サイズよりMMSが小さければ、送信サイズを修正
	if(i_inst->uip_connr.peer_mss<s){
		s=i_inst->uip_connr.peer_mss;
	}
	//送信サイズよりpeerのウインドウサイズが小さければ修正
	if(i_inst->uip_connr.peer_win<s){
		s=i_inst->uip_connr.peer_win;
	}
	//送信サイズより、データサイズが小さければ、送信サイズを修正
	if(i_len<s){
		s=i_len;
	}
	//ACK番号の計算
	next_ack=i_inst->uip_connr.snd_nxt32+s+((i_tcpf&(TCP_FIN|TCP_SYN))!=0x00?1:0);
	txq->rto=i_inst->uip_connr.rto;
	txq->time_of_sent=NyLPC_cStopwatch_now();

	//パケットの書き込み
	setPacket(i_inst,&(txq->data),i_tcpf,i_buf,s);
	//シーケンス番号の更新
	i_inst->uip_connr.snd_nxt32=next_ack;
	//Peerのウインドウサイズを更新
	i_inst->uip_connr.peer_win-=s;
	//ACK番号の返却
	*o_ack=txq->ackno=NyLPC_HTONL(next_ack);
	NyLPC_cMutex_unlock(&(i_inst->_smutex));
	NyLPC_cUipService_sendIPv4Tx(buf);
	return s;
}
/**
 * RSTを1フレームだけ送信します。
 * この関数は、クローズドステータスのソケットにしてからコールします。
 * この関数は、アンロック状態でコールしてね。
 */
static void sendRst(NyLPC_TcTcpSocket_t* i_inst)
{
	NyLPC_TcIPv4Payload_t ipv4;
	NyLPC_TUInt16 s;
	void* buf;

	NyLPC_Assert(i_inst->tcpstateflags==UIP_CLOSED);
	//ペイロードライタの初期化
	NyLPC_cIPv4Payload_initialize(&ipv4);

	//IPヘッダ+10バイトくらい。
	buf=NyLPC_cUipService_allocTxBuf((SIZE_OF_IPv4_TCPIP_HEADER)+5,&s);

	NyLPC_cMutex_lock(&(i_inst->_smutex));
	NyLPC_cIPv4Payload_setTxBuf(&ipv4,buf);
	i_inst->uip_connr.snd_nxt32++;
	setPacket(i_inst,&ipv4,TCP_RST|TCP_ACK,buf,0);
	NyLPC_cMutex_unlock(&(i_inst->_smutex));

	NyLPC_cUipService_sendIPv4Tx(buf);
	NyLPC_cUipService_releaseTxBuf(buf);
	NyLPC_cIPv4Payload_finalize(&ipv4);
	return;
}



/**
 * 受信データを排他処理してバッファに書き込む。
 * 十分な空き領域がない場合、失敗する。
 * この関数は、ロックして実行してください。
 */
static NyLPC_TBool addRecvData(NyLPC_TcTcpSocket_t* i_inst,void* i_data,NyLPC_TUInt16 i_data_size)
{
	//受信データサイズを確認
	if(NyLPC_cFifoBuffer_getSpace(&(i_inst->rxbuf))>=i_data_size){
		//バッファに格納可能なら、格納。
		NyLPC_cFifoBuffer_push(&(i_inst->rxbuf),i_data,i_data_size);
	}else{
		//エラー:ドロップする。
		return NyLPC_TBool_FALSE;
	}
	return NyLPC_TBool_TRUE;
}




/**
 * Public function
 */

/**
 * この関数は、UIP_SYN_RCVDステータスのソケットを、ESTABLISHEDへ遷移させます。
 * cTcpListener_listen関数を通過したインスタンスに実行してください。
 * この関数は、アプリケーションが呼び出します。
 * @return
 *
 */
NyLPC_TBool NyLPC_cTcpSocket_accept(NyLPC_TcTcpSocket_t* i_inst,NyLPC_TUInt32 i_wait_in_msec)
{
	volatile NyLPC_TUInt8 f;
	NyLPC_TUInt32 sq;
	NyLPC_TcStopwatch_t sw;

	NyLPC_cStopwatch_initialize(&sw);
	//ステータスチェック
	f=i_inst->tcpstateflags;
	switch(f)
	{
	case UIP_ESTABLISHED:
		return NyLPC_TBool_TRUE;
	case UIP_SYN_RCVD:
		//処理対象
		break;
	default:
		return NyLPC_TBool_FALSE;
	}
	NyLPC_cStopwatch_startExpire(&sw,i_wait_in_msec);
	if(sendWithRetransmit(i_inst,TCP_SYN|TCP_ACK,NULL,0,&sw,&sq)==0){
		//ちょっと待つ。
		taskYIELD();
		//キューにあるTXが消えるのを待つ。
		if(waitForTxRemove(i_inst,sq,&sw)){
			//ACK受信に成功して、TXが消失
			NyLPC_cStopwatch_finalize(&sw);
			return NyLPC_TBool_TRUE;
		}
	}
	//ロックして、強制的なステータス遷移
	NyLPC_cMutex_lock(&(i_inst->_smutex));
	f=i_inst->tcpstateflags;
	if(f!=UIP_CLOSED){
		i_inst->tcpstateflags=UIP_CLOSED;
	}
	NyLPC_cMutex_unlock(&(i_inst->_smutex));
	//もし、強制CLOSE遷移であれば、RSTも送信。
	if(f!=UIP_CLOSED){
		sendRst(i_inst);
	}
	return NyLPC_TBool_FALSE;
}


/**
 * この関数は、ソケットの受信バッファの読み取り位置と、読み出せるデータサイズを返却します。
 * 関数はポインターを返却するだけで、バッファの読み取り位置をシークしません。
 * シークするにはNyLPC_cTcpSocket_pseekを使います。
 */
NyLPC_TInt32 NyLPC_cTcpSocket_precv(NyLPC_TcTcpSocket_t* i_inst,const void** o_buf_ptr,NyLPC_TUInt32 i_wait_msec)
{
	volatile NyLPC_TUInt8 st;
	NyLPC_TUInt16 rlen;
	//タイマを生成
	NyLPC_TcStopwatch_t sw;
	NyLPC_cStopwatch_initialize(&sw);

	//ESTABLISHED以外の場合は、エラー。
	NyLPC_cStopwatch_setNow(&sw);
	while(NyLPC_cStopwatch_elapseInMsec(&sw)<i_wait_msec)
	{
		//読み出しバッファ情報のコピー

		//MUTEX LOCK
		NyLPC_cMutex_lock(&(i_inst->_smutex));
		st=i_inst->tcpstateflags;
		rlen=NyLPC_cFifoBuffer_getLength(&(i_inst->rxbuf));
		*o_buf_ptr=NyLPC_cFifoBuffer_getPtr(&(i_inst->rxbuf));
		//MUTEX UNLOCK
		NyLPC_cMutex_unlock(&(i_inst->_smutex));

		//バッファが空の場合は、ステータスチェック。ESTABLISHEDでなければ、エラー(PASVCLOSE等の場合)
		switch(st){
		case UIP_ESTABLISHED:
			if(rlen>0){
				//バッファにパケットがあれば返却
				NyLPC_cStopwatch_finalize(&sw);
				return rlen;
			}
			break;
		case UIP_CLOSE_WAIT:
			if(rlen>0){
				//バッファにパケットがあれば返却
				NyLPC_cStopwatch_finalize(&sw);
				return rlen;
			}
			//引き続きエラー処理
		default:
			//他の場合はエラー
			NyLPC_cStopwatch_finalize(&sw);
			return -1;
		}
		//タスクスイッチ
		taskYIELD();
	};
	//規定時間内に受信が成功しなかった。
	NyLPC_cStopwatch_finalize(&sw);
	return 0;
}
/**
 * 受信バッファをシークします。
 * シーク後に、遅延ACKを送出します。
 */
void NyLPC_cTcpSocket_pseek(NyLPC_TcTcpSocket_t* i_inst,NyLPC_TUInt16 i_seek)
{
	NyLPC_TcIPv4Payload_t ipv4payload;
	void* buf;
	NyLPC_TUInt16 s;
	NyLPC_ArgAssert(i_seek<=NyLPC_cFifoBuffer_getLength(&(i_inst->rxbuf)));
	if(i_seek==0){
		return;
	}
	//ペイロードライタの初期化
	NyLPC_cIPv4Payload_initialize(&ipv4payload);

	//ACK送信バッファの取得
	buf=NyLPC_cUipService_allocTxBuf((SIZE_OF_IPv4_TCPIP_HEADER)+5,&s);

	//MUTEX LOCK
	NyLPC_cMutex_lock(&(i_inst->_smutex));

	//受信バッファを読み出しシーク
	NyLPC_cFifoBuffer_pop(&(i_inst->rxbuf),i_seek);
	//ACKパケットの生成
	NyLPC_cIPv4Payload_setTxBuf(&ipv4payload,buf);
	setPacket(i_inst,&ipv4payload,TCP_ACK,buf,0);
	NyLPC_cMutex_unlock(&(i_inst->_smutex));
	//ACK送信
	NyLPC_cUipService_sendIPv4Tx(buf);
	NyLPC_cUipService_releaseTxBuf(buf);
	//ペイロードライタの破棄
	NyLPC_cIPv4Payload_finalize(&ipv4payload);
}

/**
 * See header file.
 */
void* NyLPC_cTcpSocket_allocSendBuf(NyLPC_TcTcpSocket_t* i_inst,NyLPC_TUInt16 i_hint,NyLPC_TUInt16* o_buf_size,NyLPC_TUInt32 i_wait_in_msec)
{
	NyLPC_TUInt16 s;
	void* buf;
	NyLPC_TcStopwatch_t sw;

	NyLPC_cStopwatch_initialize(&sw);
	NyLPC_cStopwatch_startExpire(&sw,i_wait_in_msec);

	//送信バッファを取得
	buf=NyLPC_cUipService_allocTxBuf(i_hint+(SIZE_OF_IPv4_TCPIP_HEADER),&s);
//ここ、要求サイズとpeerのwinのうち、小さいほうを割り当てたほうが良くない？
	NyLPC_cMutex_lock(&(i_inst->_smutex));
	//ペイロードがある場合のみ、相手のwindowサイズが0以上になるのを待つ。
	while(i_inst->uip_connr.peer_win==0){
		NyLPC_cMutex_unlock(&(i_inst->_smutex));
		//時間切れならエラー。
		if(NyLPC_cStopwatch_isExpired(&sw)){
			NyLPC_cStopwatch_finalize(&sw);
			return NULL;
		}
		taskYIELD();
		NyLPC_cMutex_lock(&(i_inst->_smutex));
	}
	//送信バッファを基準とした送信サイズを計算
	s-=SIZE_OF_IPv4_TCPIP_HEADER;
	//送信サイズよりMMSが小さければ、送信サイズを修正
	if(i_inst->uip_connr.peer_mss<s){
		s=i_inst->uip_connr.peer_mss;
	}
	//送信サイズよりpeerのウインドウサイズが小さければ修正
	if(i_inst->uip_connr.peer_win<s){
		s=i_inst->uip_connr.peer_win;
	}
	NyLPC_cMutex_unlock(&(i_inst->_smutex));
	//バッファサイズ確定。
	*o_buf_size=s;
	NyLPC_cStopwatch_finalize(&sw);
	return (NyLPC_TUInt8*)buf+SIZE_OF_IPv4_TCPIP_HEADER;
}


/**
 * 事前にAllocしたTxパケットを送信します。
 * このAPIはゼロコピー送信をサポートするためのものです。
 * @param i_buf_ptr
 * allocSendBufで取得したメモリを指定します。
 */
NyLPC_TBool NyLPC_cTcpSocket_psend(NyLPC_TcTcpSocket_t* i_inst,const void* i_buf_ptr,int i_len,NyLPC_TUInt32 i_wait_in_msec)
{
	struct NyLPC_TcTcpSocket_TxQItem* txq;
	void* buf;
	NyLPC_TcStopwatch_t sw;

	//ESTABLISHEDでなければエラー
	if(i_inst->tcpstateflags!=UIP_ESTABLISHED){
		//ESTABLISHEDでなければエラー
		return NyLPC_TBool_FALSE;
	}
	//送信データ0ならエラー
	if(i_len<1){
		return NyLPC_TBool_TRUE;
	}
	NyLPC_cStopwatch_initialize(&sw);
	NyLPC_cStopwatch_startExpire(&sw,i_wait_in_msec);

	//先頭ポインタは、i_buf-sizeof(SIZE_OF_IPv4_TCPIP_HEADER)固定
	buf=(NyLPC_TUInt8*)i_buf_ptr-SIZE_OF_IPv4_TCPIP_HEADER;
	NyLPC_cMutex_lock(&(i_inst->_smutex));
	//送信キューの取得
	txq=getTxQ(i_inst,&sw);
	//送信キューが取れなかった。
	if(txq==NULL){
		//シーケンス番号をロールバックできないので、エラーとする。
		NyLPC_cMutex_unlock(&(i_inst->_smutex));
		NyLPC_cUipService_releaseTxBuf(buf);
		NyLPC_cStopwatch_finalize(&sw);
		return NyLPC_TBool_FALSE;
	}
	//IPv4ペイロードの書き込み
	NyLPC_cIPv4Payload_setTxBuf(&(txq->data),buf);
	//allocをした時点でwin,mssは考慮されているので、そのままそうしんしる。
	//ACK番号の計算
	txq->rto=i_inst->uip_connr.rto;
	txq->time_of_sent=NyLPC_cStopwatch_now();
	//パケットヘッダの生成
	NyLPC_cIPv4Payload_initTcpTx(&(txq->data),0x05,((UIP_TCPH_LEN) / 4),i_len);
	NyLPC_cIPv4Payload_setTcpTxHeaderByConnection(&(txq->data),&(i_inst->uip_connr),TCP_ACK|TCP_PSH);
	NyLPC_cIPv4Payload_setTcpWnd(&(txq->data),NyLPC_cFifoBuffer_getSpace(&(i_inst->rxbuf)));
	NyLPC_cIPv4Payload_closeTcpTxPacket(&(txq->data));
	//シーケンス番号の更新
	i_inst->uip_connr.snd_nxt32=i_inst->uip_connr.snd_nxt32+i_len;
	//Peerのウインドウサイズを更新
	i_inst->uip_connr.peer_win-=i_len;
	//ACK番号の返却
	txq->ackno=NyLPC_HTONL(i_inst->uip_connr.snd_nxt32);
	NyLPC_cMutex_unlock(&(i_inst->_smutex));
	NyLPC_cUipService_sendIPv4Tx(buf);
	NyLPC_cStopwatch_finalize(&sw);
	return NyLPC_TBool_TRUE;
}

/**
 * See header file.
 */
NyLPC_TInt16 NyLPC_cTcpSocket_send(NyLPC_TcTcpSocket_t* i_inst,const void* i_buf_ptr,int i_len,NyLPC_TUInt32 i_wait_in_msec)
{
	NyLPC_TUInt32 sq;
	NyLPC_TInt32 r;
	NyLPC_TcStopwatch_t sw;
	//ESTABLISHEDでなければエラー
	if(i_inst->tcpstateflags!=UIP_ESTABLISHED){
		//ESTABLISHEDでなければエラー
		return -1;
	}
	if(i_len<1){
		return 0;
	}
	NyLPC_cStopwatch_initialize(&sw);
	NyLPC_cStopwatch_startExpire(&sw,i_wait_in_msec);
	//PSHACKを送信する。
	r=sendWithRetransmit(i_inst,TCP_PSH|TCP_ACK,i_buf_ptr,i_len,&sw,&sq);
	NyLPC_cStopwatch_finalize(&sw);
	//失敗時も何もしない？強制CLOSEしなくて平気？
	return r;
}


void NyLPC_cTcpSocket_close(NyLPC_TcTcpSocket_t* i_inst,NyLPC_TUInt32 i_wait_in_msec)
{
	NyLPC_TcStopwatch_t sw;
	volatile NyLPC_TUInt8 f;
	NyLPC_TUInt32 sq;
	NyLPC_cStopwatch_initialize(&sw);
	NyLPC_cStopwatch_startExpire(&sw,i_wait_in_msec);
	NyLPC_cMutex_lock(&(i_inst->_smutex));

	f=i_inst->tcpstateflags;
	//ステータスチェック
	switch(f)
	{
	case UIP_CLOSED:
		//閉じている。
		goto ReturnWithUnlock;
	case UIP_ESTABLISHED:
		//アクティブクローズ。
		i_inst->tcpstateflags=UIP_FIN_WAIT_1;
		//送信のために一旦解除
		NyLPC_cMutex_unlock(&(i_inst->_smutex));
		//FINの送信
		if(sendWithRetransmit(i_inst,TCP_FIN|TCP_ACK,NULL,0,&sw,&sq)==0){
			//ちょっと待つ。
			taskYIELD();
			//TXの消去待ち
			if(waitForTxRemove(i_inst,sq,&sw)){
				//再ロック
				NyLPC_cMutex_lock(&(i_inst->_smutex));
				//タイムアウトするか、UIP_CLOSED、もしくはTIME_WAITに遷移するのを待つ。(遷移はRxprocで自動的に実行。)
				while(!NyLPC_cStopwatch_isExpired(&sw)){
					switch(i_inst->tcpstateflags)
					{
					case UIP_TIME_WAIT:
						i_inst->tcpstateflags=UIP_CLOSED;
					case UIP_CLOSED:
						NyLPC_Assert(i_inst->txbuf.rp==i_inst->txbuf.wp);
						//成功。
						goto ReturnWithUnlock;
					case UIP_FIN_WAIT_1:
					case UIP_FIN_WAIT_2:
					case UIP_CLOSING:
						//一時的なアンロック
						NyLPC_cMutex_unlock(&(i_inst->_smutex));
						taskYIELD();
						NyLPC_cMutex_lock(&(i_inst->_smutex));
					default:
						break;
					}
				}
				NyLPC_cMutex_unlock(&(i_inst->_smutex));
			}
		}
		break;
	case UIP_CLOSE_WAIT:
		//LAST_ACKへ遷移
		i_inst->tcpstateflags=UIP_LAST_ACK;
		//送信のために一旦解除
		NyLPC_cMutex_unlock(&(i_inst->_smutex));
		if(sendWithRetransmit(i_inst,TCP_FIN|TCP_ACK,NULL,0,&sw,&sq)==0){
			//ちょっと待つ。
			taskYIELD();
			//TXの消去待ち
			if(waitForTxRemove(i_inst,sq,&sw)){
				//再ロック
				NyLPC_cMutex_lock(&(i_inst->_smutex));
				//TX消去後にCLOSEDに遷移していればOK
				if(i_inst->tcpstateflags==UIP_CLOSED)
				{
					NyLPC_Assert(i_inst->txbuf.rp==i_inst->txbuf.wp);
					goto ReturnWithUnlock;
				}
				NyLPC_cMutex_unlock(&(i_inst->_smutex));
			}
		}
		//エラー。RSTで切断。
		break;
	default:
		NyLPC_cMutex_unlock(&(i_inst->_smutex));
		NyLPC_Warning();
		break;
	}
	if(i_inst->_smutex._lock_count>0){
		NyLPC_Warning();
	}
	//このパスに到達するのは、FIN送信/ACKに成功したにも拘らず、規定時間内にCLOSEDに遷移しなかった場合。
	//コネクションを強制遷移して、RST
	NyLPC_cMutex_lock(&(i_inst->_smutex));
	f=i_inst->tcpstateflags;
	if(f!=UIP_CLOSED){
		i_inst->tcpstateflags=UIP_CLOSED;
	}
	NyLPC_cMutex_unlock(&(i_inst->_smutex));
	//もし、強制CLOSE遷移であれば、RSTも送信。
	if(f!=UIP_CLOSED){
		sendRst(i_inst);
	}
	NyLPC_cStopwatch_finalize(&sw);
	return;
ReturnWithUnlock:
	NyLPC_cMutex_unlock(&(i_inst->_smutex));
	NyLPC_cStopwatch_finalize(&sw);
	return;
}

/**
 * 定期的に実行する関数。最低でも1s単位で実行してください。
 * 動作
 */
void NyLPC_cTcpSocket_periodic(
	NyLPC_TcTcpSocket_t* i_inst)
{
	int i;
	struct NyLPC_TcTcpSocket_TxQItem* q=i_inst->txbuf.txq;
	NyLPC_TcStopwatch_t sw;
	NyLPC_TUInt32 now;
	int rp;
	NyLPC_cStopwatch_initialize(&sw);
	now=NyLPC_cStopwatch_now();
	//MUTEX LOCK
	NyLPC_cMutex_lock(&(i_inst->_smutex));
	if(i_inst->tcpstateflags==UIP_CLOSED)
	{
		//CLOSEDなら、バッファ開放。
		resetTxQWithUnlock(i_inst);
	}else if(i_inst->txbuf.rp==i_inst->txbuf.wp){
		//再送信パケットがなければ何もしないよ。
		NyLPC_cMutex_unlock(&(i_inst->_smutex));
	}else if(i_inst->uip_connr.peer_win==0){
		//peer_winが0の場合は何もしない。
		NyLPC_cMutex_unlock(&(i_inst->_smutex));
	}else{
		//再送信処理
		rp=i_inst->txbuf.rp;
		NyLPC_cStopwatch_set(&sw,q[rp].time_of_sent);
		if(NyLPC_cStopwatch_elapseInMsec(&sw)>q[rp].rto*1000){
			//最古のパケットの送信時間をチェックして、タイムアウトが発生したら、再送時間と送信時刻をセット
			//最古パケットRTOを2倍。
			q[rp].rto*=2;
			for(i=rp;i!=i_inst->txbuf.wp;i=(i+1)%NyLPC_TcTcpSocket_NUMBER_OF_TXQ){
				q[i].time_of_sent=now;
			}
			if(q[rp].rto>64){
				//最古のRTOが64秒を超えたら、CLOSED
				i_inst->tcpstateflags =UIP_CLOSED;
				resetTxQWithUnlock(i_inst);
				sendRst(i_inst);
			}else{
				//規定時間内なら、再送処理
				for(i=rp;i!=i_inst->txbuf.wp;i=(i+1)%NyLPC_TcTcpSocket_NUMBER_OF_TXQ){
					NyLPC_cUipService_sendIPv4Tx(NyLPC_cIPv4Payload_getBuf(&(q[i].data)));
				}
				NyLPC_cMutex_unlock(&(i_inst->_smutex));
			}
		}else{
			NyLPC_cMutex_unlock(&(i_inst->_smutex));
		}
	}
	NyLPC_cStopwatch_finalize(&sw);
	return;
}


/**
 * この関数は、rxパケットを処理して、ソケットの状態を更新します。
 * uipサービスタスクが実行する関数です。
 * o_ippのペイロードに、応答ペイロードを設定することがあります。
 * この関数はNyLPC_cTcpSocket_periodicと排他実行すること。
 */
NyLPC_TBool NyLPC_cTcpSocket_parseRx(
	NyLPC_TcTcpSocket_t* i_inst,
	NyLPC_TcIPv4Payload_t* o_ipp)
{
	int i,s;
	NyLPC_TUInt16 tmp16;
	NyLPC_TUInt16 data_size;
	NyLPC_TUInt8 in_tcpflag=o_ipp->payload.tcp->flags;
	void* tcp_data_offset;
	NyLPC_TBool is_new_packet;
	int num_of_noack;
	void* dlist[NyLPC_TcTcpSocket_NUMBER_OF_TXQ];
	NyLPC_TBool ret;

	//パラメータの計算

	tmp16=NyLPC_TTcpHeader_getHeaderLength(o_ipp->payload.tcp);
	//TCPペイロードの長さは、IPパケットの長さ-(IPヘッダ+TCPヘッダ)
	data_size=NyLPC_TIPv4Header_getPacketLength(o_ipp->header)-NyLPC_TIPv4Header_getHeaderLength(o_ipp->header)-tmp16;
	//TCPデータオフセット
	tcp_data_offset=o_ipp->payload.rawbuf+tmp16;

	//インスタンスをロックする。
	NyLPC_cMutex_lock(&(i_inst->_smutex));

	//RSTのチェック。RST受信時は、状態にかかわらず、CLOSEDステータスに移行する。
	if (in_tcpflag & TCP_RST)
	{
		i_inst->tcpstateflags =UIP_CLOSED;
		goto DROP;
	}


	is_new_packet=NyLPC_ntohl(o_ipp->payload.tcp->seqno32)==i_inst->uip_connr.rcv_nxt32;


	//OPTIONの反映

	//MSSの取得
	if(NyLPC_TTcpHeader_getTcpMmsOpt(o_ipp->payload.tcp,&tmp16)){
		//取得で着たら更新
		i_inst->uip_connr.peer_mss=tmp16;
	}
	//受信パケットを元に、未ACKパケットの数を計算
	num_of_noack=getNumOfSending(i_inst,o_ipp->payload.tcp->ackno32);//i_inst->txbuf.num_of_txq;

	//ステータス毎のACK応答
	switch(i_inst->tcpstateflags)
	{
	case UIP_SYN_RCVD:
		//ACKを受信したら、ESTABLISHEDへ。
		//すべてのパケットをACKしたかで判定。()
		if(num_of_noack==0){
			i_inst->tcpstateflags=UIP_ESTABLISHED;
		}else{
			//それ以外のパケットはドロップする。
			break;//goto DROP;
		}
		//新しいパケットがなければ、無応答
		if(!is_new_packet){
			break;//goto DROP;
		}
		//引き続き、ESTABLISHEDの処理へ。
	case UIP_ESTABLISHED:
		if(data_size>0){
			if(is_new_packet){
				if(addRecvData(i_inst,tcp_data_offset,data_size)){
					//通常のACK返却
					i_inst->uip_connr.rcv_nxt32+=data_size;
				}else{
					//失敗したときは必要に応じて単純ACK
				}
			}
		}
		//MSSとWNDの更新
		i_inst->uip_connr.peer_mss = i_inst->uip_connr.default_mss;
		//どちらにしろ、ACK送信
		if(is_new_packet && (in_tcpflag & TCP_FIN)){
			//FINがあるときは、ステータスをCLOSE_WAITへセットして、ACKを返す。
			i_inst->tcpstateflags = UIP_CLOSE_WAIT;
			i_inst->uip_connr.rcv_nxt32++;
		}
		break;
	case UIP_CLOSE_WAIT:
		//必要に応じたACK応答
		break;
	case UIP_LAST_ACK:
		//ACK(by FIN)が得られたなら、CLOSEDへ。
		if(num_of_noack==0){
			i_inst->tcpstateflags=UIP_CLOSED;
		}
		//必要に応じたACK応答
		break;
	case UIP_FIN_WAIT_1:
		//FIN受信->CLOSINGへ
		if(is_new_packet && (in_tcpflag & TCP_FIN)){
			i_inst->uip_connr.rcv_nxt32++;
			if(num_of_noack==0){
				//FINとACKを受信
				i_inst->tcpstateflags=UIP_TIME_WAIT;
			}else{
				//FINのみ
				i_inst->tcpstateflags=UIP_CLOSING;
			}
		}else if(num_of_noack==0){
			//ACKのみ
			i_inst->tcpstateflags=UIP_FIN_WAIT_2;
		}
		//必要に応じたACK応答
		break;
	case UIP_FIN_WAIT_2:
		//FIN受信->TIME_WAITへ(pureACK)
		if(is_new_packet && (in_tcpflag & TCP_FIN)){
			i_inst->uip_connr.rcv_nxt32++;
			i_inst->tcpstateflags=UIP_TIME_WAIT;
		}
		break;
	case UIP_CLOSING:
		//ACK受信したら、TIME_WAITへ
		if(num_of_noack==0){
			i_inst->tcpstateflags=UIP_TIME_WAIT;
		}
		break;
	case UIP_CLOSED:
		//何もできない。何もしない。
		break;//goto DROP;
	case UIP_TIME_WAIT:
		//最終ACKを送り続ける。
		break;
	default:
		goto DROP;
	}
	//ウインドウサイズを更新
	i_inst->uip_connr.peer_win=NyLPC_ntohs(o_ipp->payload.tcp->wnd16);
	//新しいパケットがきた場合は、再送キューのACKを更新する。
	if(is_new_packet){
		//再送キューのACKを更新
		updateTxAck(i_inst,NyLPC_htonl(i_inst->uip_connr.rcv_nxt32));
	}
	if(in_tcpflag & TCP_ACK){
		//再送パケットキューから送信済みのデータを回収(後で開放)
		NyLPC_Trace();
		s=updateTxQByIndex(i_inst,o_ipp->payload.tcp->ackno32,dlist);
		NyLPC_Trace();
	}else{
		s=0;
	}
	ret=NyLPC_TBool_FALSE;
	if(((in_tcpflag&(TCP_FIN|TCP_SYN))!=0x00) ||
		((!is_new_packet) && (data_size>0)))
	{
		setPacket(i_inst,o_ipp,TCP_ACK,NULL,0);
		ret=NyLPC_TBool_TRUE;
	}else{
		ret=NyLPC_TBool_FALSE;
	}
	NyLPC_cMutex_unlock(&(i_inst->_smutex));
	//取り外したTXメモリの開放
	for(i=0;i<s;i++){
		//取り外したTXメモリを開放
		NyLPC_cUipService_releaseTxBuf(dlist[i]);
	}
NyLPC_Trace();
	return ret;
DROP:
	//ACKしたパケットを送信キューから削除
	NyLPC_cMutex_unlock(&(i_inst->_smutex));
NyLPC_Trace();
	return NyLPC_TBool_FALSE;
}






